#!/bin/bash

# Get the absolute path of this script
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT_PATH="${SCRIPT_DIR}/$(basename "${BASH_SOURCE[0]}")"

# Configuration parameters
rc_path="${rc_path:-${HOME}/.bashrc}"
softwares_path="${softwares_path:-/opt/softwares}"
gh_proxy_prefix=${gh_proxy_prefix:-"https://ghproxy.net/"}

prog=$(basename "$0")
software_script_home="${SCRIPT_DIR}/softwares"

# Logging functions
_info() {
  echo "[$(date +'%Y-%m-%d %H:%M:%S')]: $*"
}

_warn() {
  echo -e "\033[1;33m[$(date +'%Y-%m-%d %H:%M:%S')]: $*\033[0m" >&2
}

_err() {
  echo -e "\033[1;31m[$(date +'%Y-%m-%d %H:%M:%S')]: $*\033[0m" >&2
  exit 1
}

# Discover available software scripts
discover_softwares() {
  local softwares=()
  if [[ -d "$software_script_home" ]]; then
    for script in "$software_script_home"/*.sh; do
      if [[ -f "$script" ]] && [[ -x "$script" ]]; then
        local software_name=$(basename "$script" .sh)
        softwares+=("$software_name")
      fi
    done
  fi
  echo "${softwares[@]}"
}

# Get list of supported software
get_supported_softwares() {
  discover_softwares
}

# Check if software is supported
_check_software_supported() {
  local software_name="$1"
  local supported_softwares=($(get_supported_softwares))

  for sw in "${supported_softwares[@]}"; do
    if [[ "$sw" == "$software_name" ]]; then
      return 0
    fi
  done
  return 1
}

# Get software script path
_get_software_script() {
  local software_name="$1"
  local script_path="${software_script_home}/${software_name}.sh"

  if [[ -f "$script_path" ]] && [[ -x "$script_path" ]]; then
    echo "$script_path"
  else
    echo ""
  fi
}

# Execute software script function
execute_software_function() {
  local software_name="$1"
  local function_name="$2"
  local script_path=$(_get_software_script "$software_name")

  if [[ -z "$script_path" ]]; then
    _err "Software script not found: $software_name"
  fi

  # Source the script and execute the function
  source "$script_path"

  case "$function_name" in
  "get_info")
    get_software_info
    ;;
  "install")
    execute_install_software "$softwares_path"
    ;;
  "uninstall")
    execute_uninstall_software "$softwares_path"
    ;;
  "check")
    check_installed "$softwares_path"
    ;;
  "set_config")
    set_rc_config "$rc_path"
    ;;
  *)
    _err "Unknown function: $function_name"
    ;;
  esac
}

# Check if software is installed
_check_software_installed() {
  local software_name="$1"

  # Execute the check function from the software script
  if execute_software_function "$software_name" "check" >/dev/null 2>&1; then
    return 0
  else
    return 1
  fi
}

# Interactive software selection
_interactive_select_software() {
  local action="$1" # install or uninstall
  local keyword="$2"
  local supported_softwares=($(get_supported_softwares))
  local filtered_softwares=()
  local selected_software=""

  # Filter software by keyword
  if [[ -n "$keyword" ]]; then
    for sw in "${supported_softwares[@]}"; do
      if [[ "$sw" == *"$keyword"* ]]; then
        filtered_softwares+=("$sw")
      fi
    done
  else
    filtered_softwares=("${supported_softwares[@]}")
  fi

  # Check if no software matches
  if [[ ${#filtered_softwares[@]} -eq 0 ]]; then
    _err "No software found matching keyword: $keyword"
  fi

  # If only one software matches, use it directly
  if [[ ${#filtered_softwares[@]} -eq 1 ]]; then
    selected_software="${filtered_softwares[0]}"
    echo "$selected_software"
    return 0
  fi

  # Multiple matches, show interactive selection
  echo ""
  echo "Multiple software found matching \"$keyword\":"
  echo "================================"
  for i in "${!filtered_softwares[@]}"; do
    local status=""
    if _check_software_installed "${filtered_softwares[i]}"; then
      status="(installed)"
    else
      status="(not installed)"
    fi
    printf "%2d) %-15s %s\\n" "$((i + 1))" "${filtered_softwares[i]}" "$status"
  done
  echo "================================"

  # Get user selection
  while true; do
    echo -n "Select software to $action (1-${#filtered_softwares[@]}, or 'q' to quit): "
    read -r choice

    if [[ "$choice" == "q" || "$choice" == "Q" ]]; then
      echo "Operation cancelled"
      exit 0
    fi

    if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#filtered_softwares[@]} ]]; then
      selected_software="${filtered_softwares[$((choice - 1))]}"
      break
    else
      echo "Invalid selection. Please enter a number between 1 and ${#filtered_softwares[@]}"
    fi
  done

  echo "$selected_software"
}

# Get software information
get_software_info() {
  local software_name="$1"
  if [[ -n "$software_name" ]]; then
    if ! _check_software_supported "$software_name"; then
      _err "Unsupported software: $software_name"
    fi
    execute_software_function "$software_name" "get_info"
  else
    # List all software information
    local supported_softwares=($(get_supported_softwares))
    for sw in "${supported_softwares[@]}"; do
      echo "=== $sw ==="
      execute_software_function "$sw" "get_info"
      echo
    done
  fi
}

# Display help information
usage() {
  local supported_softwares=($(get_supported_softwares))

  echo "================================================================================"
  echo "Software Management Tool - $prog"
  echo ""
  echo "Usage: $prog <command> [software-name] [options]"
  echo ""
  echo "Commands:"
  echo "  list                  List all supported software and their installation status"
  echo "  info [software]       Show detailed information about software"
  echo "  install <software>    Install specified software"
  echo "  uninstall <software>   Uninstall specified software"
  echo "  config                Show current configuration"
  echo "  -h, --help            Show this help message"
  echo ""
  echo "Options:"
  echo "  --force, -f          Force reinstall (even if already installed)"
  echo "  --version <version>  Specify installation version"
  echo ""
  echo "Supported Software:"
  for sw in "${supported_softwares[@]}"; do
    echo "  - $sw"
  done
  echo ""
  echo "Examples:"
  echo "  $prog list                    # List all software status"
  echo "  $prog info nvim              # Show nvim information"
  echo "  $prog install nvim           # Install nvim"
  echo "  $prog install tssh --force   # Force reinstall tssh"
  echo "  $prog uninstall nvim         # Uninstall nvim"
  echo "================================================================================"
}

# Show configuration information
show_config() {
  local supported_softwares=($(get_supported_softwares))

  echo "================ Configuration ================"
  echo "Config file path: $rc_path"
  echo "Software installation path: $softwares_path"
  echo "GitHub proxy prefix: $gh_proxy_prefix"
  echo "Script root directory: $prog_root"
  echo "Software scripts directory: $software_script_home"
  echo "Number of supported software: ${#supported_softwares[@]}"
  echo "Supported software: ${supported_softwares[*]}"
  echo "=============================================="
}

# List software status
list_softwares() {
  local supported_softwares=($(get_supported_softwares))

  echo "================ Software Status ================"
  for software_name in "${supported_softwares[@]}"; do
    if _check_software_installed "$software_name"; then
      echo "✓ $software_name - Installed"
    else
      echo "○ $software_name - Not installed"
    fi
  done
  echo "================================================"
}

# Install software
install_software() {
  local software_name=""
  local force_install=false
  local custom_version=""

  # Parse options and keyword
  while [[ $# -gt 0 ]]; do
    case "$1" in
    --force | -f)
      force_install=true
      shift
      ;;
    --version)
      if [[ $# -gt 1 ]]; then
        custom_version="$2"
        shift 2
      else
        _err "--version option requires a version number"
      fi
      ;;
    -*)
      # Handle unknown options
      if [[ -z "$software_name" ]]; then
        _err "Unknown option: $1"
      else
        break
      fi
      ;;
    *)
      # This is the software name or keyword
      software_name="$1"
      shift
      ;;
    esac
  done

  # If no software name provided, show interactive selection
  if [[ -z "$software_name" ]]; then
    _info "No software specified. Showing interactive selection..."
    software_name=$(_interactive_select_software "install" "")
    if [[ -z "$software_name" ]]; then
      return 0
    fi
  fi

  # Check if software is supported
  if ! _check_software_supported "$software_name"; then
    # If not directly supported, try interactive selection with keyword
    _warn "Software '$software_name' not found in supported list. Searching for matches..."
    software_name=$(_interactive_select_software "install" "$software_name")
    if [[ -z "$software_name" ]]; then
      return 0
    fi
  fi

  # Check if already installed
  if _check_software_installed "$software_name" && [[ "$force_install" != "true" ]]; then
    _warn "$software_name is already installed. Use --force option to force reinstallation"
    return 0
  fi

  # Set custom version if specified
  if [[ -n "$custom_version" ]]; then
    export "${software_name}_version"="$custom_version"
    _info "Using custom version: $custom_version"
  fi

  _info "Starting installation of $software_name..."

  # Execute installation
  if execute_software_function "$software_name" "install"; then
    # Set environment configuration
    local config_content=$(execute_software_function "$software_name" "set_config")
    local env_var_name="${software_name^^}_HOME"

    # Check if environment variable is already configured
    if grep -q "# ${env_var_name}" "$rc_path" 2>/dev/null; then
      _warn "Environment variable ${env_var_name} is already configured in $rc_path"
      _warn "Skipping environment configuration to avoid duplication"
    else
      # Add to bashrc if not already present
      echo "$config_content" >>"$rc_path"
      _info "Environment configuration added to $rc_path"
    fi

    _info "$software_name installation completed successfully"
    _info ""
    _info "IMPORTANT: Please run 'source $rc_path' to apply the environment changes"
    _info "or restart your terminal session for the changes to take effect"
  else
    _err "$software_name installation failed"
  fi
}

# Uninstall software
uninstall_software() {
  local software_name=""

  # Parse software name
  if [[ $# -gt 0 ]]; then
    software_name="$1"
  fi

  # If no software name provided, show interactive selection
  if [[ -z "$software_name" ]]; then
    _info "No software specified. Showing interactive selection..."
    software_name=$(_interactive_select_software "uninstall" "")
    if [[ -z "$software_name" ]]; then
      return 0
    fi
  fi

  # Check if software is supported
  if ! _check_software_supported "$software_name"; then
    # If not directly supported, try interactive selection with keyword
    _warn "Software '$software_name' not found in supported list. Searching for matches..."
    software_name=$(_interactive_select_software "uninstall" "$software_name")
    if [[ -z "$software_name" ]]; then
      return 0
    fi
  fi

  # Check if already installed
  if ! _check_software_installed "$software_name"; then
    _warn "$software_name is not installed, no need to uninstall"
    return 0
  fi

  _info "Starting uninstallation of $software_name..."

  # Execute uninstallation
  if execute_software_function "$software_name" "uninstall"; then
    # Remove environment configuration from bashrc
    local env_var_name="${software_name^^}_HOME"
    if [[ -f "$rc_path" ]]; then
      _info "Removing $env_var_name configuration from $rc_path"
      sed -i "/^# ${env_var_name}/,/^# ${env_var_name}/d" "$rc_path" 2>/dev/null
      sed -i "/${env_var_name}=/d" "$rc_path" 2>/dev/null
      sed -i "/export PATH.*${env_var_name}/d" "$rc_path" 2>/dev/null
    fi

    _info "$software_name uninstallation completed. Please run 'source $rc_path' to apply changes"
  else
    _err "$software_name uninstallation failed"
  fi
}

# Main function
main() {
  if [[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]]; then
    usage
    exit 0
  fi

  case "$1" in
  "list") list_softwares ;;
  "info") get_software_info "${@:2}" ;;
  "install") install_software "${@:2}" ;;
  "uninstall") uninstall_software "${@:2}" ;;
  "config") show_config ;;
  *)
    _err "Unknown command: $1"
    usage
    exit 1
    ;;
  esac
}

main "$@"
